package listeners

import (
	"blockchains-aio/conf"
	"blockchains-aio/ethrpc"
	"blockchains-aio/gin/misc"
	"blockchains-aio/ns3"
	"blockchains-aio/stack"
	"blockchains-aio/util"
	"context"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"github.com/shurcooL/go/ioutil"
	"log"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"
)

func RegisterNs3(router *gin.Engine) {
	rootGroup := getRootGroup(router)
	cci := rootGroup.Group("/ns3")

	cci.Any("/stats", showStats)

	cci.POST("/pow", doRunNs3OnPow)
	cci.DELETE("/pow", doDestroyExistedNs3)
	//cci.POST("/poa", doRunNs3OnPow)

	cci.DELETE("/stack2/instance", doDestroyExistedNs3Instance)

	//-----ns3pow2 consensus as followed
	cci.POST("/stack2/tmpl/ns3pow", doGenStackConfigForNs3Pow)
	cci.POST("/stack2/ns3pow", doDockStackDeployForNs3Pow)

	//-----poa2 consensus as followed
	cci.POST("/stack2/tmpl/ns3poa", doGenStack2ConfigForNs3Poa)
	cci.POST("/stack2/ns3poa", doDockStack2DeployForNs3Poa)

	//------ pos2 + ns3 consensus as followed
	cci.POST("/stack2/tmpl/ns3pos", doGenStack2ConfigForNs3Pos)
	cci.POST("/stack2/ns3pos", doDockStack2DeployForNs3Pos)

	cci.GET("/topology", doPrefetchTopology)
	cci.POST("/settings/access", doUpdateAccessSettings)

	cci.GET("/eth/connection", doDetectEthConnection)
}

func doGenStack2ConfigForNs3Pos(ctx *gin.Context) {
	var config stack.SoloPOS2
	consensus := ctx.GetHeader("Consensus-Template-Type") //ns3pos2

	if err := ctx.BindJSON(&config); err != nil {
		ctx.JSON(http.StatusBadRequest, err)
		return
	}
	config.Nfs, _ = util.ExpandTilde(conf.GetNfsFolder())

	pos2Tmpl := fmt.Sprintf("%s/%s.tmpl", conf.GetStackTemplateFolder(), consensus)
	expendedPoaTmpl, _ := util.ExpandTilde(pos2Tmpl)
	err := stack.ParsePOS2Template(ctx.Writer, expendedPoaTmpl, config)
	if err != nil {
		//ctx.JSON(http.StatusBadRequest, fmt.Sprintf("%v", err))
		ctx.JSON(http.StatusBadRequest, NewErrorMessage(Stack, 1, "doGenStack2ConfigForPos", fmt.Sprintf("%v", err)).Json())
	}
}

func doDockStack2DeployForNs3Pos(ctx *gin.Context) {
	chainname := ctx.PostForm("chainname")
	nodeNum := ctx.PostForm("nodeNum")
	consensus := ctx.PostForm("consensus")
	duration := ctx.PostForm("duration")

	log.Printf("recieved {chainname:%s,nodeNum:%s,consensus:%s}", chainname, nodeNum, consensus)

	if len(chainname) == 0 || len(nodeNum) == 0 {
		//ctx.JSON(400, fmt.Sprintf("Failed to get chainname or nodeNum"))
		ctx.JSON(400, NewErrorMessage(Stack, 1, "doDockStack2DeployForNs3Pos", fmt.Sprintf("Failed to get chainname or nodeNum")).Json())
		return
	}
	if _, err := strconv.Atoi(nodeNum); err != nil {
		//ctx.JSON(400, fmt.Sprintf("%v is not a number", nodeNum))
		ctx.JSON(400, NewErrorMessage(Stack, 2, "doDockStack2DeployForNs3Pos", fmt.Sprintf("%v is not a number", nodeNum)).Json())
		return
	}
	//nodeNumber, _ := strconv.Atoi(nodeNum)
	if len(consensus) == 0 {
		consensus = "pos2"
	}

	file, _ := ctx.FormFile("file")
	f, err := file.Open()
	if err != nil {
		//ctx.JSON(400, fmt.Sprintf("Failed to open yaml:%v", err))
		ctx.JSON(400, NewErrorMessage(Stack, 3, "doDockStack2DeployForNs3Pos", fmt.Sprintf("Failed to open yaml:%v", err)).Json())
		return
	}
	defer f.Close()

	p := make([]byte, file.Size)
	if _, err := f.Read(p); err != nil {
		//ctx.JSON(400, fmt.Sprintf("Failed to read yaml: %v", err))
		ctx.JSON(400, NewErrorMessage(Stack, 4, "doDockStack2DeployForNs3Pos", fmt.Sprintf("Failed to read yaml: %v", err)).Json())
		return
	}

	if err := stack.MkChainDirAndCopyDirs_POS2(chainname, consensus); err != nil {
		//ctx.JSON(400, fmt.Sprintf("Failed to copy dirs: %v", err))
		ctx.JSON(400, NewErrorMessage(Stack, 5, "doDockStack2DeployForNs3Pos", fmt.Sprintf("Failed to copy dirs: %v", err)).Json())
		return
	}

	chainFolder, _ := util.ExpandTilde(fmt.Sprintf("%s/%s", conf.GetNfsFolder(), chainname))
	//yaml := chainFolder + "/" + chainname + ".yml"
	yaml := chainFolder + "/" + conf.DefaultComposeFileName

	if err := os.WriteFile(yaml, p, 0644); err != nil {
		// ctx.JSON(400, fmt.Sprintf("Failed to write file %s: %v", yaml, err))
		ctx.JSON(400, NewErrorMessage(Stack, 6, "doDockStack2DeployForNs3Pos", fmt.Sprintf("Failed to write file %s: %v", yaml, err)).Json())
		return
	}

	log.Printf(fmt.Sprintf("working directory: %s", chainFolder))
	out, err := util.DockerComposeDeployUp3(chainFolder)
	if err != nil {
		//ctx.JSON(400, fmt.Sprintf("Failed to deploy stack: %v", err))
		ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Stack, conf.BrindDownContainersInCompose, "doDockStack2DeployForPos", fmt.Sprintf("Failed to deploy stack: %v", err)).Json())
		return
	}

	log.Println("start ns3 in an unblocked way")
	out, err = util.StartPosNs3Sh(conf.GetNs3(), duration, false)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Stack, 9, "doDockStack2DeployForNs3Pos", fmt.Sprintf("Failed to create ns3: %v %s", err, out.String())).Json())
		return
	}

	ctx.JSON(200, out.String())
}

func doDetectEthConnection(ctx *gin.Context) {
	ethurl := ctx.Query("ethurl")
	if ethurl != "" {
		ecw := ethrpc.New(ethurl)
		if err := ecw.Dial(); err != nil {
			ecw.Close()
			ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Ns3, 1, "doDetectEthConnection", fmt.Sprintf("eth dialing error: %v", err)).Json())
			return
		}
		block, err := ecw.GetLatestBlock(context.Background())
		if err != nil {
			ecw.Close()
			ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Ns3, 2, "doDetectEthConnection", fmt.Sprintf("eth getting block error: %v", err)).Json())
			return
		}

		ecw.Close()
		ctx.JSON(http.StatusOK, block.Number())
		return
	}

	ctx.JSON(http.StatusBadRequest, NewErrorMessage(Ns3, 2, "doDetectEthConnection", fmt.Sprintf("bad request ethurl")).Json())
}

func doUpdateAccessSettings(ctx *gin.Context) {
	Ns3StatsPath := os.Getenv("NS3_STATS_PATH")
	const AsName = "access_settings"
	if Ns3StatsPath == "" {
		ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Ns3, 1, "doUpdateAccessSettings", fmt.Sprintf("NS3_STATS_PATH env not found")).Json())
		return
	}

	file, _, err := ctx.Request.FormFile("file")
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Ns3, 2, "doUpdateAccessSettings", fmt.Sprintf("uploading error: %v", err)).Json())
		return
	}

	err = ioutil.WriteFile(path.Join(Ns3StatsPath, AsName), file)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Ns3, 2, "doUpdateAccessSettings", fmt.Sprintf("writing file error: %v", err)).Json())
		return
	}

	ctx.JSON(http.StatusOK, "")
}

func doPrefetchTopology(ctx *gin.Context) {
	Ns3StatsPath := os.Getenv("NS3_STATS_PATH")
	TopologyTxtName := "Topology.txt"
	node := ctx.Query("node")
	if Ns3StatsPath == "" {
		ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Ns3, 1, "doPrefetchTopology", fmt.Sprintf("NS3_STATS_PATH env not found")).Json())
		return
	}

	TopologyTxtFullPath := path.Join(Ns3StatsPath, TopologyTxtName)
	if !util.IsPathExists(TopologyTxtFullPath) {
		ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Ns3, 2, "doPrefetchTopology", fmt.Sprintf("%v not found", TopologyTxtFullPath)).Json())
		return
	}

	topology := ns3.GetWorker().GetAllNodesTopology()
	if node != "" {
		foundTopology := scanAllNodesTopologyForNode(topology, node)
		ctx.JSON(http.StatusOK, foundTopology)
		return
	}

	ctx.JSON(http.StatusOK, topology)
}

func scanAllNodesTopologyForNode(topology []ns3.NodeTopology, node string) (foundTopology []ns3.NodeTopology) {
	n := node
	foundTopology = make([]ns3.NodeTopology, 0)

	if !strings.HasPrefix(n, "节点 ") {
		n = "节点 " + n
	}

	for _, top := range topology {
		//log.Printf("NodeTo:%s,NodeFrom:%s,node:%s", top.NodeTo, top.NodeFrom, n)
		if strings.Compare(top.NodeTo, n) == 0 || strings.Compare(top.NodeFrom, n) == 0 {
			foundTopology = append(foundTopology, top)
		}
	}

	return
}

// consensus: ns3poa2
func doGenStack2ConfigForNs3Poa(ctx *gin.Context) {
	var ns3Config stack.NS3POA2
	consensus := ctx.GetHeader("Consensus-Template-Type")

	if err := ctx.BindJSON(&ns3Config); err != nil {
		ctx.JSON(http.StatusBadRequest, err)
		return
	}
	ns3Config.Nfs, _ = util.ExpandTilde(conf.GetNfsFolder())

	poaTmpl := fmt.Sprintf("%s/%s.tmpl", conf.GetStackTemplateFolder(), consensus)
	expendedPoaTmpl, _ := util.ExpandTilde(poaTmpl)
	err := stack.ParsePOATemplate(ctx.Writer, expendedPoaTmpl, ns3Config)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, NewErrorMessage(Stack, 1, "doGenStackns3ConfigForPow", fmt.Sprintf("%v", err)).Json())
	}
}

func doDockStack2DeployForNs3Poa(ctx *gin.Context) {
	var (
		//hasCompose = false
		hasGenesis = false
	)
	chainname := ctx.PostForm("chainname")
	nodeNum := ctx.PostForm("nodeNum")
	consensus := ctx.PostForm("consensus")
	duration := ctx.PostForm("duration")

	log.Printf("recieved {chainname:%s,nodeNum:%s,consensus:%s,duration:%s}", chainname, nodeNum, consensus, duration)

	if len(chainname) == 0 || len(nodeNum) == 0 || len(duration) == 0 {
		ctx.JSON(400, fmt.Sprintf("Failed to get chainname or nodeNum or duration"))
		return
	}
	if _, err := strconv.Atoi(nodeNum); err != nil {
		ctx.JSON(400, fmt.Sprintf("%v is not a number", nodeNum))
		return
	}
	//nodeNumber, _ := strconv.Atoi(nodeNum)
	if len(consensus) == 0 {
		consensus = "poa"
	}

	p, err := readFormFileBytes(ctx, "file")
	if err != nil {
		ctx.JSON(400, fmt.Sprintf("%v", err))
		return
	}
	genesisBytes, err := readFormFileBytes(ctx, "genesis")
	if err != nil {
		log.Printf("warning: an exception was ignored while doing custom genesis: %v", err)
	}
	if len(genesisBytes) > 0 {
		hasGenesis = true
		log.Println("custom genesis size:", len(genesisBytes))
	}

	if err := MakePathesAndCopyData(chainname, consensus); err != nil {
		ctx.JSON(400, fmt.Sprintf("Failed to copy dirs: %v", err))
		return
	}

	chainFolder, _ := util.ExpandTilde(fmt.Sprintf("%s/%s", conf.GetNfsFolder(), chainname))
	yaml := chainFolder + "/" + conf.DefaultComposeFileName
	genesis := chainFolder + "/" + "data/config/genesis.json"

	if err := os.WriteFile(yaml, p, 0644); err != nil {
		ctx.JSON(400, fmt.Sprintf("Failed to write file %s: %v", yaml, err))
		return
	}

	if hasGenesis {
		if err := os.WriteFile(genesis, genesisBytes, 0644); err != nil {
			ctx.JSON(400, fmt.Sprintf("Failed to write custom genesis file %s: %v", genesis, err))
			return
		}
	}

	log.Printf(fmt.Sprintf("working directory: %s", chainFolder))
	//out, err := util.DockerComposeDeployUpConfigFile(yaml, chainFolder)
	out, err := util.DockerComposeDeployUp3(chainFolder)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Stack, conf.BrindDownContainersInCompose, "doDockStack2DeployForPow", fmt.Sprintf("Failed to deploy stack: %v %s", err, out.String())).Json())
		return
	}

	log.Println("start ns3 in an unblocked way")
	out, err = util.StartNs3Sh(conf.GetNs3(), duration, false)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Stack, 9, "createNs3Instance", fmt.Sprintf("Failed to create ns3 instance: %v %s", err, out.String())).Json())
		return
	}
	ctx.JSON(200, out.String())
}

// consensus: ns3pow2
func doGenStackConfigForNs3Pow(ctx *gin.Context) {
	var ns3Pow2Config stack.NS3POW2
	consensus := ctx.GetHeader("Consensus-Template-Type")

	if err := ctx.BindJSON(&ns3Pow2Config); err != nil {
		ctx.JSON(http.StatusBadRequest, err)
		return
	}
	ns3Pow2Config.Nfs, _ = util.ExpandTilde(conf.GetNfsFolder())

	poaTmpl := fmt.Sprintf("%s/%s.tmpl", conf.GetStackTemplateFolder(), consensus)
	expendedPoaTmpl, _ := util.ExpandTilde(poaTmpl)
	err := stack.ParsePOATemplate(ctx.Writer, expendedPoaTmpl, ns3Pow2Config)
	if err != nil {
		ctx.JSON(http.StatusBadRequest, NewErrorMessage(Stack, 1, "doGenStackns3ConfigForPow", fmt.Sprintf("%v", err)).Json())
	}
}

func doDockStackDeployForNs3Pow(ctx *gin.Context) {
	var (
		hasGenesis = false
	)
	chainname := ctx.PostForm("chainname")
	nodeNum := ctx.PostForm("nodeNum")
	consensus := ctx.PostForm("consensus")
	duration := ctx.PostForm("duration")

	log.Printf("recieved {chainname:%s,nodeNum:%s,consensus:%s,duration:%s}", chainname, nodeNum, consensus, duration)

	if len(chainname) == 0 || len(nodeNum) == 0 || len(duration) == 0 {
		ctx.JSON(400, fmt.Sprintf("Failed to get chainname or nodeNum or duration"))
		return
	}
	if _, err := strconv.Atoi(nodeNum); err != nil {
		ctx.JSON(400, fmt.Sprintf("%v is not a number", nodeNum))
		return
	}

	p, err := readFormFileBytes(ctx, "file")
	if err != nil {
		ctx.JSON(400, fmt.Sprintf("%v", err))
		return
	}
	genesisBytes, err := readFormFileBytes(ctx, "genesis")
	if err != nil {
		log.Printf("warning: an exception was ignored while doing custom genesis: %v", err)
	}
	if len(genesisBytes) > 0 {
		hasGenesis = true
		log.Println("custom genesis size:", len(genesisBytes))
	}

	if err := MakePathesAndCopyData(chainname, consensus); err != nil {
		ctx.JSON(400, fmt.Sprintf("Failed to copy dirs: %v", err))
		return
	}

	chainFolder, _ := util.ExpandTilde(fmt.Sprintf("%s/%s", conf.GetNfsFolder(), chainname))
	yaml := chainFolder + "/" + conf.DefaultComposeFileName
	genesis := chainFolder + "/" + "data/config/genesis.json"

	if err := os.WriteFile(yaml, p, 0644); err != nil {
		ctx.JSON(400, fmt.Sprintf("Failed to write file %s: %v", yaml, err))
		return
	}

	if hasGenesis {
		if err := os.WriteFile(genesis, genesisBytes, 0644); err != nil {
			ctx.JSON(400, fmt.Sprintf("Failed to write custom genesis file %s: %v", genesis, err))
			return
		}
	}

	log.Printf(fmt.Sprintf("working directory: %s", chainFolder))
	//out, err := util.DockerComposeDeployUpConfigFile(yaml, chainFolder)
	out, err := util.DockerComposeDeployUp3(chainFolder)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Stack, conf.BrindDownContainersInCompose, "doDockStack2DeployForPow", fmt.Sprintf("Failed to deploy stack: %v %s", err, out.String())).Json())
		return
	}

	log.Println("start ns3 in an unblocked way")
	out, err = util.StartNs3Sh(conf.GetNs3(), duration, false)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Stack, 9, "createNs3Instance", fmt.Sprintf("Failed to create ns3: %v %s", err, out.String())).Json())
		return
	}

	ctx.JSON(200, out.String())
}

func doDestroyExistedNs3Instance(ctx *gin.Context) {
	chainname := ctx.Query("chain")

	chainFolder, _ := util.ExpandTilde(fmt.Sprintf("%s/%s", conf.GetNfsFolder(), chainname))
	theChainYaml := chainFolder + "/" + conf.DefaultComposeFileName
	log.Println("Path existed or not:", theChainYaml)
	if util.IsPathExists(theChainYaml) {
		var _, err = util.DockerComposeDown1(chainFolder)
		if err != nil {
			ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Stack, 4, "doDestroyExistedNs3Instance", fmt.Sprintf("Failed to bring down compose task: %v", err)).Json())
			return
		}

		err = os.RemoveAll(chainFolder)
		if err != nil {
			ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Stack, 6, "doDestroyExistedNs3Instance", fmt.Sprintf("Failed to remove chain folder: %v", err)).Json())
			return
		}

	}

	//out, err := util.StartNs3Sh(conf.GetNs3(), "1200", true)
	//if err != nil {
	//	ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Stack, 9, "doDestroyExistedNs3Instance", fmt.Sprintf("Failed to remove ns3: %v %s", err, out.String())).Json())
	//	return
	//}
	log.Println("attempt to remove ns3")
	util.StartNs3Sh(conf.GetNs3(), "1200", true)
	util.StartPosNs3Sh(conf.GetNs3(), "1200", true)

	ctx.JSON(http.StatusOK, "")
}

func doDestroyExistedNs3(ctx *gin.Context) {
	worker := ns3.GetWorker()
	worker.IsRebuiding = true

	nodeNum := ctx.Query("nodeNum")
	out, err := ns3.Ns3Destroy(conf.GetNs3(), nodeNum)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Ns3, 1, "doDestroyExistedNs3", fmt.Sprintf("Failed to destroy ns3: %v %s", err, out.String())).Json())
		return
	}

	worker.RemoveDataFiles()
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Ns3, 2, "doDestroyExistedNs3", fmt.Sprintf("Failed to remove data files: %v %s", err, out.String())).Json())
		return
	}
	worker.Flush()
	ctx.JSON(http.StatusOK, out.String())
}

func doRunNs3OnPow(ctx *gin.Context) {
	worker := ns3.GetWorker()

	nodeNum := ctx.PostForm("nodeNum")
	duration := ctx.PostForm("duration")

	log.Printf("recieved {nodeNum:%s, duration:%s}", nodeNum, duration)

	out, err := ns3.Ns3Create(conf.GetNs3(), nodeNum, duration)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Ns3, 1, "doRunNs3OnPow", fmt.Sprintf("Failed to create ns3: %v %s", err, out.String())).Json())
		return
	}
	out, err = ns3.Ns3Emu(conf.GetNs3(), nodeNum, duration)
	if err != nil {
		ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Ns3, 2, "doRunNs3OnPow", fmt.Sprintf("Failed to emulate ns3: %v %s", err, out.String())).Json())
		return
	}

	worker.IsRebuiding = false
	log.Println("worker.IsRebuiding=", worker.IsRebuiding)
	ctx.JSON(http.StatusOK, out.String())
}

//func doRunNs3OnPoa(ctx *gin.Context) {
//	nodeNum := ctx.PostForm("nodeNum")
//	duration := ctx.PostForm("duration")
//
//	log.Printf("recieved {nodeNum:%s, duration:%s}", nodeNum, duration)
//
//	ctx.JSON(http.StatusOK, out.String())
//}

func showStats(ctx *gin.Context) {
	worker := ns3.GetWorker()
	if worker.IsRebuiding {
		ctx.JSON(http.StatusLocked, NewErrorMessage(Ns3, 1, "showStats", fmt.Sprintf("failed to showStats because of the ns rebuilding")).Json())
		return
	}

	err := startWS(ctx.Writer, ctx.Request, ctx)
	if err != nil {
		//ctx.JSON(http.StatusInternalServerError, fmt.Sprintf("call %s error: %v", "calculateTps", err))
		ctx.JSON(http.StatusInternalServerError, NewErrorMessage(Ns3, 2, "showStats", fmt.Sprintf("failed to showStats: %v", err)).Json())
		return
	}
}

func startWS(w http.ResponseWriter, r *http.Request, ctx *gin.Context) error {
	bg := context.Background()
	worker := ns3.GetWorker()

	ethurl := ctx.Query("ethurl")
	var ecw *ethrpc.EthClientWrapper
	if ethurl != "" {
		ecw := ethrpc.New(ethurl)
		if err := ecw.Dial(); err != nil {
			log.Printf("fail to dial %s", ethurl)
		}
	}

	if !worker.IsReady() {
		return errors.New("ns3 data files not found at all")
	}

	c, err := misc.Upgrader.Upgrade(w, r, nil)
	if err != nil {
		return err
	}

	worker.StartFollowersConcurrently(bg)
	defer worker.Stop()
	defer c.Close()
	for {
		//stats := getTestStats()
		//err = c.WriteMessage(websocket.TextMessage, stats.ToBytes())
		if worker.IsRebuiding {
			break
		}

		stats := worker.Collect(11, ecw)
		//		if !stats.IsRubbish() {
		err = c.WriteMessage(websocket.TextMessage, stats.ToBytes())
		if err != nil {
			log.Printf("warning: %v", err)
			break
		}

		_, _, err := c.ReadMessage()
		if err != nil {

			break
		}
		//		}
		//time.Sleep(2 * time.Second)
	}
	worker.Stop()

	return nil
}

func MakePathesAndCopyData(chain string, consensus string) error {
	// ./data/config
	// ./ns3.sh
	templateResFolder, _ := util.ExpandTilde(conf.GetStackTemplateResFolder(consensus))
	chainFolder, _ := util.ExpandTilde(fmt.Sprintf("%s/%s", conf.GetNfsFolder(), chain))
	if err := util.CreateAllDir(chainFolder); err != nil {
		return errors.New(fmt.Sprintf("Failed to create %s: %v", chainFolder, err))
	}

	if err := util.CoyDir(templateResFolder+"/data", chainFolder+"/data"); err != nil {
		return errors.New(fmt.Sprintf("Fail to copy %s /data/: %v", consensus, err))
	}
	//log.Printf("Done copy dir from:%s to:%s", templateResFolder+"/data", chainFolder+"/data")

	//if err := util.CopyFile(templateResFolder+"/ns3.sh", chainFolder+"/ns3.sh"); err != nil {
	//	return errors.New(fmt.Sprintf("Fail to copy %s /ns3.sh/: %v", consensus, err))
	//}

	return nil
}

func getTestStats() ns3.Stats {
	return ns3.Stats{
		Interval: 2,
		SpacePeriodView: []ns3.Space{
			{
				T:    50.000000,
				Node: "节点 0",
				X:    59.208341,
				Y:    43.031065,
			},
			{
				T:    50.000000,
				Node: "节点 1",
				X:    50.453435,
				Y:    92.272942,
			},
		},
		NodesAccumulativeTrafficView: []ns3.NodePhyMacRxTx{
			{
				Node:       "节点 0",
				PhyRxBytes: 110,
				PhyTxBytes: 160,
				MacRxBytes: 6,
				MacTxBytes: 22,
			},
			{
				Node:       "节点 1",
				PhyRxBytes: 110,
				PhyTxBytes: 14,
				MacRxBytes: 14,
				MacTxBytes: 14,
			},
		},
		InstantAccumulativeTrafficView: ns3.AccumulativeTraffic{
			PhyRx: ns3.IAT{
				T:     3.000009,
				Bytes: 344,
			},
			PhyTx: ns3.IAT{
				T:     1.005025,
				Bytes: 11150,
			},
			MacRx: ns3.IAT{
				T:     1.000004,
				Bytes: 94,
			},
			MacTx: ns3.IAT{
				T:     1.505703,
				Bytes: 5445,
			},
		},
		//PhyView: []ns3.NodePhyRxTx{
		//	{
		//		Node:    "节点 0",
		//		RxBytes: 110,
		//		TxBytes: 160,
		//	},
		//	{
		//		Node:    "节点 1",
		//		RxBytes: 150,
		//		TxBytes: 3,
		//	},
		//},
		//MacView: []ns3.NodeMacRxTx{
		//	{
		//		Node:    "节点 0",
		//		RxBytes: 44,
		//		TxBytes: 160,
		//	},
		//	{
		//		Node:    "节点 1",
		//		RxBytes: 55,
		//		TxBytes: 3,
		//	},
		//},
		LogView: []string{
			"API server listening at: 127.0.0.1:41863",
			"WARNING: undefined behavior - version of Delve is too old for Go version go1.23.3 (maximum supported version 1.22)",
			"=== RUN   TestFollow",
			"2025/01/06 20:09:54 Seeked /home/francis/ns3/sp.txt - &{Offset:0 Whence:0}",
		},
	}
}
